package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/beevik/etree"
)

func AddMenu(menubar *etree.Element, actions []*etree.Element, result []*Menu) []*Menu {
	var menu []Menu
	ms := GetWidgetsByClass(menubar, "QMenu")
	for _, v := range ms {
		menu = append(menu, Menu{
			Title: GetPropertyToString(v, "title"),
			Ele:   v,
			Name:  v.SelectAttr("name").Value,
		})

	}
	// 读取add action
	addmenus := menubar.SelectElements("addaction")
	for _, v := range addmenus {
		for _, m := range menu {
			if v.SelectAttr("name").Value == m.Name {
				//windowui.Menu = append(windowui.Menu, m)
				result = append(result, &m)
			}
		}
		for _, m := range actions {
			if v.SelectAttr("name").Value == m.SelectAttr("name").Value {
				//windowui.Menu = append(windowui.Menu, m)
				result = append(result, &Menu{
					Ele:   m,
					Name:  m.SelectAttr("name").Value,
					Title: GetPropertyToString(m, "text"),
				})
			}
		}
	}
	for _, v := range result {
		v.Elements = AddMenu(v.Ele, actions, v.Elements)
	}
	return result
}

func AddWidgets(ele *etree.Element, result []*WindowUiElement, item *etree.Element) ([]*WindowUiElement, error) {
	var err error
	var property ElementProperty
	property.MaxSize = MaxSize
	property.MinSize = MinSize
	property.Enabled = true
	property.Visible = true

	property.Row = -1
	property.Column = -1
	property.RowSpan = -1
	property.ColumnSpan = -1

	property.Font.Family = "Segoe UI"
	property.Font.Point = 9
	switch ele.Tag {
	case "widget":
		w := WindowUiElement{
			Ele:      ele,
			Name:     ele.SelectAttr("name").Value,
			Tag:      ele.Tag,
			Layout:   "",
			Property: property,
			Class:    ele.SelectAttr("class").Value,
		}
		switch ele.SelectAttr("class").Value {
		case "QWidget":
			fontele := GetPropertyByName(ele, "font")
			if fontele != nil {
				font := fontele.SelectElement("font")
				if font == nil {
					return nil, fmt.Errorf("font元素不存在")
				}
				family := font.ChildElements()
				for _, v := range family {
					switch v.Tag {
					case "family":
						w.Property.Font.Family = v.Text()
					case "pointSize":
						point, _ := strconv.Atoi(v.Text())
						w.Property.Font.Point = point
					}
				}
			}
			layout := ele.SelectElement("layout")
			if layout == nil { // 这里处理不是布局的元素，一般是绝对定位
				eles := GetElementsExceptProperty(ele)
				for _, v := range eles {
					// 读取
					bounds, err := GetPropertyToBounds(v, "geometry")
					if err != nil {
						return nil, err
					}
					w.Property.Bounds = bounds
					if v.SelectAttr("class").Value == "QWidget" {
						// 继续递归
						w.Elements, err = AddWidgets(v, w.Elements, nil)
						if err != nil {
							return nil, err
						}
					}
				}
			} else {
				bounds, _ := GetPropertyToBounds(ele, "geometry")
				w.Property.Bounds = bounds

				w.Property.LayoutLeftMargin = GetPropertyToNumber(layout, "leftMargin", 9)
				w.Property.LayoutRightMargin = GetPropertyToNumber(layout, "rightMargin", 9)
				w.Property.LayoutTopMargin = GetPropertyToNumber(layout, "topMargin", 9)
				w.Property.LayoutBottomMargin = GetPropertyToNumber(layout, "bottomMargin", 9)

				w.Property.LayoutVerticalSpacing = GetPropertyToNumber(layout, "verticalSpacing", 6)
				w.Property.LayoutHorizontalSpacing = GetPropertyToNumber(layout, "horizontalSpacing", 6)

				if GetPropertyToNumber(layout, "spacing", -1) != -1 {
					// 包含spacing，设置h和v为spacing
					w.Property.LayoutVerticalSpacing = GetPropertyToNumber(layout, "spacing", 6)
					w.Property.LayoutHorizontalSpacing = GetPropertyToNumber(layout, "spacing", 6)
				}

				w.Layout = layout.SelectAttr("class").Value
				w.Property.AttributeTitle = GetAttributeToString(ele, "title")
				for _, v := range layout.ChildElements() {
					if v.Tag == "item" {
						if v.ChildElements()[0].SelectAttr("class").Value == "QToolButton" {
							// 跳过这次循环
							continue
						}
						w.Elements, err = AddWidgets(v.ChildElements()[0], w.Elements, v)
						if err != nil {
							return nil, err
						}
					}
				}
			}

		case "QTabWidget":
			w.Property.CurrentIndex = GetPropertyToNumber(ele, "currentIndex", 0)
			wids := GetElementsExceptProperty(ele)
			for _, v := range wids {
				w.Elements, err = AddWidgets(v, w.Elements, nil)
				if err != nil {
					return nil, err
				}
			}
		default:
			w.Property.Enabled = GetPropertyToBool(ele, "enabled", true)
			w.Property.Text = GetPropertyToString(ele, "text")
			w.Property.Checkable = GetPropertyToBool(ele, "checkable", true)
			w.Property.Checked = GetPropertyToBool(ele, "checked", true)
			w.Property.ReadOnly = GetPropertyToBool(ele, "readOnly", false)
			w.Property.ToolTip = GetPropertyToString(ele, "toolTip")
			w.Property.SelectMode = GetPropertyToEnum(ele, "selectionMode", "")
			w.Property.Editable = GetPropertyToBool(ele, "editable", true)
			w.Property.TextAlignment = GetPropertyToSet(ele, "alignment", "")
			w.Property.EchoMode = GetPropertyToEnum(ele, "echoMode", "")
			w.Property.MaxiNum = GetPropertyToNumber(ele, "maximum", 99)
			w.Property.MiniMun = GetPropertyToNumber(ele, "minimum", 0)
			w.Property.Step = GetPropertyToNumber(ele, "singleStep", 1)
			w.Property.Value = GetPropertyToNumber(ele, "value", 0)
			w.Property.DisplayFormat = GetPropertyToString(ele, "displayFormat")
			w.Property.MaxiNumDate = GetPropertyToDate(ele, "maximumDate", MaxiNumDate)
			w.Property.MiniNumDate = GetPropertyToDate(ele, "minimumDate", MiniNumDate)
			w.Property.Date = GetPropertyToDate(ele, "date", time.Now())
			w.Property.Orientation = GetPropertyToEnum(ele, "orientation", "")
			fontele := GetPropertyByName(ele, "font")
			if fontele != nil {
				font := fontele.SelectElement("font")
				family := font.ChildElements()
				for _, v := range family {
					switch v.Tag {
					case "family":
						w.Property.Font.Family = v.Text()
					case "pointSize":
						point, _ := strconv.Atoi(v.Text())
						w.Property.Font.Point = point
					}
				}
			}

			if item != nil {

				for _, v := range item.Attr {
					switch v.Key {
					case "row":
						row, _ := strconv.Atoi(v.Value)
						w.Property.Row = row
					case "column":
						column, _ := strconv.Atoi(v.Value)
						w.Property.Column = column
					case "rowspan":
						rowSpan, _ := strconv.Atoi(v.Value)
						w.Property.RowSpan = rowSpan
					case "colspan":
						columnSpan, _ := strconv.Atoi(v.Value)
						w.Property.ColumnSpan = columnSpan
					case "alignment":
						w.Property.Alignment = v.Value
					}
				}
			}

		}

		result = append(result, &w)
	}
	return result, nil
}

func GetWidgetsByClass(ele *etree.Element, class string) []*etree.Element {
	widgets := ele.SelectElements("widget")
	var ret []*etree.Element
	for _, v := range widgets {
		if v.SelectAttr("class").Value == class {
			ret = append(ret, v)
		}
	}
	return ret
}

func GetWidgetByClass(ele *etree.Element, class string) *etree.Element {
	ws := GetWidgetsByClass(ele, class)
	if len(ws) == 0 {
		return nil
	}
	return ws[0]
}

func GetWidgetsByName(ele *etree.Element, class string) []*etree.Element {
	widgets := ele.SelectElements("widget")
	var ret []*etree.Element
	for _, v := range widgets {
		if v.SelectAttr("name").Value == class {
			ret = append(ret, v)
		}
	}
	return ret
}

func GetWidgetByName(ele *etree.Element, class string) *etree.Element {
	ws := GetWidgetsByName(ele, class)
	if len(ws) == 0 {
		return nil
	}
	return ws[0]
}

func GetPropertyToString(ele *etree.Element, name string) string {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("string").Text()
		}
	}
	return ""
}

func GetPropertyAttrToString(ele *etree.Element, name string, attr string) string {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("string").SelectAttr(attr).Value
		}
	}
	return ""
}

func GetAttributeToString(ele *etree.Element, name string) string {
	prots := ele.SelectElements("attribute")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("string").Text()
		}
	}
	return ""
}

func GetPropertyToBool(ele *etree.Element, name string, def bool) bool {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("bool").Text() == "true"
		}
	}
	return def
}

func GetPropertyToDate(ele *etree.Element, name string, def time.Time) time.Time {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			eles := v.ChildElements()
			year := 0
			month := 0
			day := 0
			for _, v := range eles {
				switch v.Tag {
				case "year":
					year, _ = strconv.Atoi(v.Text())
				case "month":
					month, _ = strconv.Atoi(v.Text())
				case "day":
					day, _ = strconv.Atoi(v.Text())
				}
			}
			if year != 0 && month != 0 && day != 0 {
				return def
			}
			return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)

		}
	}
	return def
}

func GetPropertyToNumber(ele *etree.Element, name string, def int) int {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			n, e := strconv.Atoi(v.SelectElement("number").Text())
			if e != nil {
				return def
			}
			return n
		}
	}
	return def
}

func GetPropertyToEnum(ele *etree.Element, name string, def string) string {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("enum").Text()
		}
	}
	return def
}

func GetPropertyToSet(ele *etree.Element, name string, def string) string {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v.SelectElement("set").Text()
		}
	}
	return def
}

func GetPropertyToBounds(ele *etree.Element, name string) (ElementBounds, error) {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			rect := v.SelectElement("rect")
			if rect == nil {
				return ElementBounds{}, fmt.Errorf("rect error")
			}
			xe := rect.SelectElement("x")
			ye := rect.SelectElement("y")
			we := rect.SelectElement("width")
			he := rect.SelectElement("height")
			x, err := strconv.Atoi(xe.Text())
			if err != nil {
				return ElementBounds{}, fmt.Errorf("x error")
			}
			y, err := strconv.Atoi(ye.Text())
			if err != nil {
				return ElementBounds{}, fmt.Errorf("y error")
			}
			width, err := strconv.Atoi(we.Text())
			if err != nil {
				return ElementBounds{}, fmt.Errorf("width error")
			}
			height, err := strconv.Atoi(he.Text())
			if err != nil {
				return ElementBounds{}, fmt.Errorf("height error")
			}
			return ElementBounds{
				X:      x,
				Y:      y,
				Width:  width,
				Height: height,
			}, nil
		}
	}
	return ElementBounds{}, fmt.Errorf("not found property")
}

func GetPropertyByName(ele *etree.Element, name string) *etree.Element {
	prots := ele.SelectElements("property")
	for _, v := range prots {
		if v.SelectAttr("name").Value == name {
			return v
		}
	}
	return nil
}
func GetElementsExceptProperty(ele *etree.Element) []*etree.Element {
	eles := ele.ChildElements()
	var ret []*etree.Element
	for _, v := range eles {
		if v.Tag != "property" {
			ret = append(ret, v)
		}
	}
	return ret
}
